#include <unistd.h>
#include <sys/types.h> 

#include "setproctitle.h"
#include "log.h"
#include "global.h"

int Init_args(int argc, char *const *argv){
    // 统计arvg所占的内存，同时检查是否不能再创建守护进程，避免二次创建
    int ret = 1;
    g_argvneedmem = 0;
    for(int i = 0; i < argc; i++){
        g_argvneedmem += strlen(argv[i]) + 1; // +1是给\0留空间
        if(strcmp(argv[i], "--no-daemon") == 0){
            ret = 0;// 设置为未启用守护进程
        }
    }

    // 统计环境变量所占的内存
    for(int i = 0; environ[i]; i++){
        g_envneedmem += strlen(environ[i]) + 1; //+1是因为末尾有\0, 是占实际内存位置的
    }

    g_os_argc = argc;// 保存参数个数
    g_os_argv = (char **) argv; // 保存参数指针

    Init_setproctitle();// 分配内存，并且把环境变量拷贝到新内存中来
    return ret;
}

//分配内存，并且把环境变量拷贝到新内存中来
void Init_setproctitle(){
    // 这里无需判断penvmen == NULL,有些编译器new会返回NULL，有些会报异常,崩溃还有助于发现问题
    gp_envmem = new char[g_envneedmem];// 分配存储环境变量的内存
    memset(gp_envmem,0,g_envneedmem);// 清空内存
    char *ptmp = gp_envmem;// 指向内存开始

    // 把原来的内存内容搬到新地方来
    // 因为修改进程的标题是通过修改argv[0]来实现的，但argv和environ在内存中是连续存放的
    // 如果直接修改argv[0]的长度超过原来的空间，可能会覆盖后面的环境变量，导致程序出错
    // 因此，将环境变量复制到新的内存区域，释放原来的空间，就可以安全地修改argv[0]的内容
    for (int i = 0; environ[i]; i++){
        size_t size = strlen(environ[i]) + 1;// 必须要+1，否则内存全乱套了，因为strlen是不包括字符串末尾的\0的
        strcpy(ptmp,environ[i]);// 把原环境变量内容拷贝到新内存
        environ[i] = ptmp;// 然后还要让新环境变量指向这段新内存
        ptmp += size;
    }
}

// 设置可执行程序标题，主要原理是覆盖argv[0]的内容
void Setproctitle(const char *title){
    size_t size = sizeof(title);
    size += g_argvneedmem;// argv参数的长度也算进来
    if(size < 1000){// 小于1000才设置
        char newTitle[1000] = {0};
        strcpy(newTitle,(const char *)title);
        strcat(newTitle," ");// 跟一个空格隔开，更清晰

        // 1计算新标题长度
        size_t ititlelen = strlen(newTitle); 
        // 2计算总的原始arvg内存的总长度（包括各种参数）
        size_t esy = g_argvneedmem + g_envneedmem;
        if( esy <= ititlelen) return;// 标题太长了，直接不改了
        // 3设置后续命令行参数为空，表示argv[]中只有一个元素。这是好习惯
        g_os_argv[1] = NULL;
        // 4把标题覆盖掉g_os_argv[0]
        char *ptmp = g_os_argv[0];
        strcpy(ptmp,newTitle);
        ptmp += ititlelen;
        // 5把剩余的原argv以及environ所占的内存全部清0,防止原有内存残留
        size_t cha = esy - ititlelen;
        memset(ptmp,0,cha);

        Log_Info(0,"设置进程名称: %s 成功!!!!", newTitle);
        return;
    }
    Log_Error(0, "尝试为进程设置名称:%s 失败!!!", title);
}